home *** CD-ROM | disk | FTP | other *** search
/ MacFormat 1999 April / macformat-075.iso / Shareware Plus / Applications / Alpha / Tcl / SystemCode / textManip.tcl < prev    next >
Encoding:
Text File  |  1999-01-28  |  29.9 KB  |  1,095 lines  |  [TEXT/ALFA]

  1. #===========================================================================
  2. # Information about a selection or window.
  3. #===========================================================================
  4. proc wordCount {} {
  5.     if {[set chars [string length [set text [getSelect]]]]} {
  6.     set lines [expr {[lindex [posToRowCol [selEnd]] 0] - [lindex [posToRowCol [getPos]] 0]}]
  7.     set text [getSelect]
  8.     } else {
  9.     set chars [maxPos]
  10.     set lines [lindex [posToRowCol $chars] 0]
  11.     set text [getText [minPos] [maxPos]]
  12.     }
  13.     regsub -all {[!=;.,\(\#\=\):\{\"\}]} $text " " text
  14.     set words [llength $text]
  15.     alertnote [format "%d chars, %d words, %d lines" $chars $words $lines]
  16. }
  17.  
  18.  
  19. # FILE: sortLines.tcl
  20. #
  21. # last update: 28/1/1999 {9:49:06 pm}
  22. #
  23. # This version of sortLines has the option of ignoring blanks/whitespace (-b)
  24. # and case-insensitive sorting (-i), or reverse sorting:
  25. #     sortLines [-b] [-i] [-r]
  26.  
  27. # COPYRIGHT:
  28. #
  29. #    Copyright © 1992,1993 by David C. Black All rights reserved.
  30. #    Portions copyright © 1990, 1991, 1992 Pete Keleher. All Rights Reserved.
  31. #
  32. #    Redistribution and use in source and binary forms are permitted
  33. #    provided that the above copyright notice and this paragraph are
  34. #    duplicated in all such forms and that any documentation,
  35. #    advertising materials, and other materials related to such
  36. #    distribution and use acknowledge that the software was developed
  37. #    by David C. Black.
  38. #
  39. #    THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
  40. #    IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
  41. #    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  42. #
  43. ################################################################################
  44.  
  45. # AUTHOR
  46. #
  47. #    David C. Black
  48. #    GEnie:    D.C.Black
  49. #    Internet: black@mpd.tandem.com (preferred)
  50. #    USnail:   6217 John Chisum Lane, Austin, TX 78749
  51. #
  52. ################################################################################
  53.  
  54. proc reverseSort {} {sortLines -r}
  55.  
  56. proc sortLines {args} {
  57.     set b_flag [lsearch $args "-b"]
  58.     if {$b_flag != -1} {
  59.     set args [lreplace $args $b_flag $b_flag]
  60.     }
  61.     incr b_flag
  62.     
  63.     set i_flag [lsearch $args "-i"]
  64.     if {$i_flag != -1} {
  65.     set args [lreplace $args $i_flag $i_flag]
  66.     }
  67.     incr i_flag
  68.     
  69.     if {[lsearch $args "-r"] >= 0} {
  70.     set mode "-decreas"
  71.     } else {
  72.     set mode "-increas"
  73.     }
  74.     
  75.     set start [getPos]
  76.     set end  [selEnd]
  77.     if {[pos::compare $start == $end]} {
  78.     alertnote "You must highlight the section you wish to sort."
  79.     return
  80.     }
  81.     if {[lookAt [pos::math $end - 1]] != "\r"} {
  82.     alertnote "The selection must consist only of complete lines."
  83.     return
  84.     }
  85.     set text [split [getText $start [pos::math $end - 1]] "\r"]
  86.     if {$b_flag > 0 || $i_flag > 0} {
  87.     foreach line $text {
  88.         if {$i_flag > 0} {
  89.         set key [string tolower $line]
  90.         } else {
  91.         set key $line
  92.         }
  93.         if {$b_flag > 0} {
  94.         regsub -all "\[ \t\]+" $key " " key
  95.         }
  96.         set orig($key) $line
  97.         lappend list $key
  98.     }
  99.     #endforeach
  100.     unset text
  101.     foreach key [lsort $mode $list] {
  102.         lappend text $orig($key)
  103.     }
  104.     #endforeach
  105.     } else {
  106.     set text [lsort $mode $text]
  107.     }
  108.     set text [join $text "\r"]
  109.     replaceText $start [pos::math $end - 1] $text
  110.     select $start $end
  111. }
  112. # Test case:
  113. #
  114. # a  black
  115. # a black cat
  116. # A  black dog
  117.  
  118.  
  119. ## 
  120.  # -------------------------------------------------------------------------
  121.  # 
  122.  # "sortParagraphs" --
  123.  # 
  124.  #  Sorts selected paragraphs according to their first 30 characters,
  125.  #  it's case insensitive and removes all non alpha-numeric characters
  126.  #  before the sort.
  127.  # -------------------------------------------------------------------------
  128.  ##
  129. proc sortParagraphs {args} {
  130.     set start [getPos]
  131.     set end  [selEnd]
  132.     if {[pos::compare $start == $end]} {
  133.     alertnote "You must highlight the section you wish to sort."
  134.     return
  135.     }
  136.     if {[lookAt [pos::math $end - 1]] != "\r"} {
  137.     alertnote "The selection must consist only of complete lines."
  138.     return
  139.     }
  140.     set text [getText $start $end]
  141.     if {[string first "•" $text] != -1} {
  142.     alertnote "Sorry, can't sort paragraphs with bullets '•'."
  143.     return
  144.     }
  145.     regsub -all "\[\r\n\]\[ \t\]*\[\r\n]" $text "\r•" text
  146.     set paras [split $text "•"]
  147.     unset text
  148.     # now each paragraph ends in \r
  149.     foreach para $paras {
  150.     set key [string tolower [string range $para 0 30]]
  151.     regsub -all {[^-a-z0-9]} $key "" key
  152.     # so we don't clobber duplicates!
  153.     while {[info exists orig($key)]} {append key "z"}
  154.     set orig($key) $para
  155.     }
  156.     unset para
  157.     foreach key [lsort [array names orig]] {
  158.     lappend text $orig($key)
  159.     }
  160.     replaceText $start $end [join $text "\r"]
  161.     select $start $end
  162. }
  163.  
  164.  
  165.  
  166. #================================================================================
  167. # Block shift left and right.
  168. #================================================================================
  169.  
  170. proc shiftLeft {} {
  171.     global shiftChar
  172.     doShiftLeft $shiftChar
  173. }
  174.  
  175. proc shiftLeftSpace {} {
  176.     doShiftLeft " "
  177. }
  178.  
  179. proc doShiftLeft {shiftChar} {
  180.     set start [lineStart [getPos]]
  181.     set end [nextLineStart [pos::math [selEnd] - 1]]
  182.     if {[pos::compare $start >= $end]} {set end [nextLineStart $start]}
  183.     
  184.     set text [split [getText $start [pos::math $end - 1]] "\r\n"]
  185.     
  186.     set textout ""
  187.     
  188.     foreach line $text {
  189.     if {[regexp ($shiftChar)(.*)$ $line "" "" c]} {
  190.         lappend textout $c
  191.     } else {
  192.         lappend textout $line
  193.     }
  194.     }
  195.     
  196.     set text [join $textout "\r"]    
  197.     replaceText $start [pos::math $end - 1] $text
  198.     select $start [pos::math $start + [expr {1 + [string length $text]}]]
  199. }
  200.  
  201. proc shiftRight {} {
  202.     global shiftChar
  203.     doShiftRight $shiftChar
  204. }
  205.  
  206. proc shiftRightSpace {} {
  207.     doShiftRight " "
  208. }
  209.  
  210. proc doShiftRight {shiftChar} {
  211.     set start [lineStart [getPos]]
  212.     set end [nextLineStart [pos::math [selEnd] - 1]]
  213.     if {[pos::compare $start >= $end]} {set end [nextLineStart $start]}
  214.     
  215.     set text [split [getText $start [pos::math $end - 1]] "\r\n"]
  216.     
  217.     set textout ""
  218.     
  219.     foreach line $text {
  220.     lappend textout $shiftChar$line
  221.     }
  222.     
  223.     set text [join $textout "\r"]    
  224.     replaceText $start [pos::math $end - 1] $text
  225.     select $start [pos::math $start + [expr {1 + [string length $text]}]]
  226. }
  227.  
  228. proc selectAll {} {
  229.     select [minPos] [maxPos]
  230. }
  231.  
  232. # Select the next or current word. If word already selected, will go to next.
  233. proc hiliteWord {} {
  234.     if {[pos::compare [getPos] != [selEnd]]} forwardChar
  235.     forwardWord
  236.     set start [getPos]
  237.     backwardWord
  238.     select $start [getPos] 
  239. }
  240.  
  241. proc twiddle {} {
  242.     set pos [getPos]
  243.     if {[pos::compare $pos == [minPos]]} return
  244.     if {[pos::compare $pos == [maxPos]] || \
  245.       [pos::compare $pos == [pos::math [nextLineStart $pos] - 1]]} {
  246.     set incr -1
  247.     } else {
  248.     set incr 0
  249.     }
  250.     if {[string length [set text [getSelect]]]} {
  251.     if {[string length $text] == 1} {
  252.         return
  253.     } else {
  254.         set sel [pos::math [selEnd] + $incr]
  255.         set one [lookAt [pos::math $sel -1]]
  256.         set two [lookAt $pos]
  257.         replaceText $pos $sel "$one[getText [pos::math $pos + 1] [pos::math $sel - 1]]$two"
  258.         select $pos $sel
  259.         return
  260.     }
  261.     }
  262.     set pos [pos::math $pos + $incr]
  263.     set one [lookAt $pos]
  264.     set two [lookAt [pos::math $pos - 1]]
  265.     replaceText [pos::math $pos - 1] [pos::math $pos + 1] "$one$two"
  266.     select  [pos::math $pos - 1] [pos::math $pos + 1]
  267. }
  268.  
  269. proc twiddleWords {} {
  270.     global wordBreakPreface wordBreak
  271.     set pos [getPos]
  272.     if {[pos::compare $pos == [maxPos]] || $pos == [pos::math [nextLineStart $pos] - 1]} {
  273.     set eol 1
  274.     } else {
  275.     set eol 0
  276.     }
  277.     if {[pos::compare [getPos] != [selEnd]]} {
  278.     set start1 [getPos]; set end2 [selEnd]
  279.     select $start1
  280.     forwardWord; set end1 [getPos]
  281.     goto $end2
  282.     backwardWord; set start2 [getPos]
  283.     } else {
  284.     if {$eol} {
  285.         backwardWord; set pos [getPos]
  286.     }
  287.     select $pos
  288.     backwardWord; set start1 [getPos]
  289.     forwardWord; set end1 [getPos]
  290.     goto $pos
  291.     forwardWord; set end2 [getPos]
  292.     backwardWord; set start2 [getPos]
  293.     }        
  294.     
  295.     if {$start1 != $start2} {
  296.     set mid [getText $end1 $start2]
  297.     replaceText $start1 $end2 "[getText $start2 $end2]$mid[getText $start1 $end1]"
  298.     select $start1 $end2
  299.     }
  300. }
  301.  
  302. # proc commentLine {} {insertPrefix}
  303. proc commentLine {} {
  304.     global mode
  305.     global ${mode}::commentCharacters
  306.     if {![catch {commentCharacters Paragraph} chars]} {
  307.     set start [lindex $chars 0]
  308.     set end [lindex $chars 1]
  309.     if {[string trim $start] == [string trim $end]} {
  310.         insertPrefix
  311.     } else {
  312.         set ext  [file extension [win::CurrentTail]]
  313.         if {($mode == "C" || $mode == "C++") && $ext != ".h" && $ext != ".c"} {
  314.         insertPrefix
  315.         } else {
  316.         beginningOfLine
  317.         insertText $start
  318.         endOfLine
  319.         insertText $end
  320.         beginningOfLine
  321.         }
  322.     }
  323.     } else {
  324.     insertPrefix
  325.     }
  326. }
  327.  
  328. proc uncommentLine {} {removePrefix}
  329. proc insertPrefix {} {doPrefix insert}
  330. proc removePrefix {} {doPrefix remove}
  331. proc doPrefix {which} {
  332.     global prefixString
  333.     if {[pos::compare [set start [getPos]] == [set end [selEnd]]]} {
  334.     set end [nextLineStart $start]
  335.     }
  336.     set start [lineStart $start]
  337.     set text [getText $start $end]
  338.     replaceText $start $end [doPrefixText $which $prefixString $text]
  339.     goto $start
  340.     endOfLine
  341. }
  342.  
  343. proc quoteChar {} {
  344.     message "Literal keystroke to be inserted:"
  345.     insertText [getChar]
  346. }
  347.  
  348. proc setPrefix {} {
  349.     global prefixString
  350.     if {[catch {prompt "New Prefix String:" $prefixString} res] == 1} return
  351.     set prefixString $res
  352. }
  353.  
  354. proc setSuffix {} {
  355.     global suffixString
  356.     if {[catch {prompt "New Suffix String:" $suffixString} res] == 1} return
  357.     set suffixString $res
  358. }
  359.  
  360. proc insertSuffix {} {doSuffix insert}
  361. proc removeSuffix {} {doSuffix remove}
  362. proc doSuffix {which} {
  363.     global suffixString
  364.     set pts [getEndpts]
  365.     set start [lindex $pts 0]
  366.     set end [lindex $pts 1]
  367.     set start [lineStart $start]
  368.     set end [nextLineStart [pos::math $end - 1]]
  369.     set text [getText $start $end]
  370.     set text [doSuffixText $which $suffixString $text]
  371.     replaceText $start $end $text
  372.     select $start [getPos]
  373. }
  374.  
  375. proc commentBox {} {
  376.  
  377.     # Preliminaries
  378.     if {[commentGetRegion Box]} { return }
  379.     
  380.     set commentList [commentCharacters Box]
  381.     if { [llength $commentList] == 0 } { return }
  382.     
  383.     set begComment [lindex $commentList 0]
  384.     set begComLen [lindex $commentList 1]
  385.     set endComment [lindex $commentList 2]
  386.     set endComLen [lindex $commentList 3]
  387.     set fillChar [lindex $commentList 4]
  388.     set spaceOffset [lindex $commentList 5]
  389.  
  390.     set aSpace " "
  391.  
  392.     # First make sure we grab a full block of lines and adjust highlight
  393.  
  394.     set start [getPos]
  395.     set start [lineStart $start]
  396.     set end [selEnd]
  397.     set end [nextLineStart [pos::math $end - 1]]
  398.     select $start $end
  399.  
  400.     # Now get rid of any tabs
  401.     
  402.     if {[pos::compare $end < [maxPos]]} {
  403.     createTMark stopComment [pos::math $end + 1]
  404.     tabsToSpaces
  405.     gotoTMark stopComment
  406.     set end [pos::math [getPos] - 1]
  407.     removeTMark stopComment
  408.     } else {
  409.     tabsToSpaces
  410.     set end [maxPos]
  411.     }
  412.     select $start $end
  413.     set text [getText $start $end]
  414.     
  415. # Next turn it into a list of lines--possibly drop an empty 'last line'
  416.  
  417. # VMD May'95: changed this code segment because it
  418. # previously had problems with empty lines in the
  419. # middle of the text to be commented
  420.  
  421.     set lineList [split $text "\r\n"]
  422.     set ll [llength $lineList]
  423.     if { [lindex $lineList end] == {} } {
  424.     set lineList [lrange $lineList 0 [expr {$ll -2}] ]
  425.     }
  426.     set numLines [llength $lineList]
  427.  
  428. # end changes.
  429.     
  430. # Find the longest line length and determine the new line length
  431.  
  432.     set maxLength 0
  433.     foreach thisLine $lineList {
  434.     set thisLength [string length $thisLine]
  435.     if { $thisLength > $maxLength } { 
  436.         set maxLength $thisLength 
  437.     }
  438.     }
  439.     set newLength [expr {$maxLength + 2 + 2*$spaceOffset}]
  440.     
  441.     # Now create the top & bottom bars and a blank line
  442.  
  443.     set topBar $begComment
  444.     for { set i 0 } { $i < [expr {$newLength - $begComLen}] } { incr i } {
  445.     append topBar $fillChar
  446.     }
  447.     set botBar ""
  448.     for { set i 0 } { $i < [expr {$newLength - $endComLen}] } { incr i } {
  449.     append botBar $fillChar
  450.     }
  451.     append botBar $endComment
  452.     set blankLine $fillChar
  453.     for { set i 0 } { $i < [expr {$newLength - 2}] } { incr i } {
  454.     append blankLine " "
  455.     }
  456.     append blankLine $fillChar
  457.     
  458.     # For each line add stuff on left and spaces and stuff on right for box sides
  459.     # and concatenate everything into 'text'.  Start with topBar; end with botBar
  460.  
  461.     set text $topBar\r$blankLine\r
  462.     
  463.     set frontStuff $fillChar
  464.     set backStuff $fillChar
  465.     for { set i 0 } { $i < $spaceOffset } { incr i } {
  466.     append frontStuff " "
  467.     set backStuff $aSpace$backStuff
  468.     }
  469.     set backStuffLen [string length $backStuff]
  470.     
  471.     for { set i 0 } { $i < $numLines } { incr i } {
  472.     set thisLine [lindex $lineList $i ]
  473.     set thisLine $frontStuff$thisLine
  474.     set thisLength [string length $thisLine]
  475.     set howMuchPad [expr {$newLength - $thisLength - $backStuffLen}]
  476.     for { set j 0 } { $j < $howMuchPad } { incr j } {
  477.         append thisLine " "
  478.     }
  479.     append thisLine $backStuff
  480.     append text $thisLine \r
  481.     }
  482.     
  483.     append text $blankLine \r $botBar \r
  484.     
  485. # Now replace the old stuff, turn spaces to tabs, and highlight
  486.  
  487.     replaceText    $start $end    $text
  488.     set    end    [pos::math $start + [string length $text]]
  489.     frontSpacesToTabs $start $end
  490. }
  491.  
  492. proc uncommentBox {} {
  493.  
  494. # Preliminaries
  495.     if {[commentGetRegion Box 1]} { return }
  496.     
  497.     set commentList [commentCharacters Box]
  498.     if { [llength $commentList] == 0 } { return }
  499.     
  500.     set    begComment [lindex $commentList    0]
  501.     set    begComLen [lindex $commentList 1]
  502.     set    endComment [lindex $commentList    2]
  503.     set    endComLen [lindex $commentList 3]
  504.     set    fillChar [lindex $commentList 4]
  505.     set    spaceOffset    [lindex    $commentList 5]
  506.     
  507.     set aSpace " "
  508.     set aTab \t
  509.  
  510.     # First make sure we grab a full block of lines
  511.  
  512.     set start [getPos]
  513.     set start [lineStart $start]
  514.     set end [selEnd]
  515.     set end [nextLineStart [pos::math $end - 1]]
  516.     set text [getText $start $end]
  517.  
  518.     # Make sure we're at the start and end of the box
  519.  
  520.     set startOK [string first $begComment $text]
  521.     set endOK [string last $endComment $text]
  522.     set textLength [string length $text]
  523.     if { $startOK != 0 || ($endOK != [expr {$textLength-$endComLen-1}] || $endOK == -1) } {
  524.     alertnote "You must highlight the entire comment box, including the borders."
  525.     return
  526.     }
  527.     
  528.     # Now get rid of any tabs
  529.     
  530.     if {[pos::compare $end < [maxPos]] } {
  531.     createTMark stopComment [pos::math $end + 1]
  532.     tabsToSpaces
  533.     gotoTMark stopComment
  534.     set end [pos::math [getPos] - 1]
  535.     removeTMark stopComment
  536.     } else {
  537.     tabsToSpaces
  538.     set end [maxPos]
  539.     }
  540.     select $start $end
  541.     set text [getText $start $end]
  542.     
  543. # Next turn it into a list of lines--possibly drop an empty 'last line'
  544.  
  545. # VMD May'95: changed this code segment because it
  546. # previously had problems with empty lines in the
  547. # middle of the text to be commented
  548.  
  549.     set lineList [split $text "\n\r"]
  550.     set ll [llength $lineList]
  551.     if { [lindex $lineList end] == {} } {
  552.     set lineList [lrange $lineList 0 [expr {$ll -2}] ]
  553.     }
  554.     set numLines [llength $lineList]
  555.  
  556. # end changes.
  557.     
  558. # Delete the first and last lines, recompute number of lines
  559.  
  560.     set lineList [lreplace $lineList [expr {$numLines-1}] [expr {$numLines-1}] ]
  561.     set lineList [lreplace $lineList 0 0 ]
  562.     set numLines [llength $lineList]
  563.     
  564.     # Eliminate 2nd and 2nd-to-last lines if they are empty
  565.  
  566.     set eliminate $fillChar$aSpace$aTab
  567.     set thisLine [lindex $lineList [expr {$numLines-1}]]
  568.     set thisLine [string trim $thisLine $eliminate]
  569.     if { [string length $thisLine] == 0 } {
  570.     set lineList [lreplace $lineList [expr {$numLines-1}] [expr {$numLines-1}] ]
  571.     }
  572.     set thisLine [lindex $lineList 0]
  573.     set thisLine [string trim $thisLine $eliminate]
  574.     if { [string length $thisLine] == 0 } {
  575.     set lineList [lreplace $lineList 0 0 ]
  576.     }
  577.     set numLines [llength $lineList]    
  578.     
  579. # For each line trim stuff on left and spaces and stuff on right and splice
  580.  
  581.     set dropFromLeft [expr {$spaceOffset+1}]
  582.     set text ""
  583.     for { set i 0 } { $i < $numLines } { incr i } {
  584.     set thisLine [lindex $lineList $i]
  585.     set thisLine [string trimright $thisLine $eliminate]
  586.     set thisLine [string range $thisLine $dropFromLeft end]
  587.     set text $text$thisLine\r
  588.     }
  589.         
  590.     # Now replace the old stuff, convert spaces back to tabs
  591.  
  592.     replaceText    $start $end    $text
  593.     set end [pos::math $start + [string    length $text]]
  594.     frontSpacesToTabs $start $end
  595. }
  596.  
  597. ## 
  598.  # -------------------------------------------------------------------------
  599.  #     
  600.  # "commentCharacters" --
  601.  #    
  602.  #    Adds the 'general' purpose characters which
  603.  #    are    used to    check if we're in a    comment    block.
  604.  #    Also has a check for an array entry like this:
  605.  #    
  606.  #    set C++::commentCharacters(General) [list "*" "//"]
  607.  #    
  608.  #    If such an entry exists, it is returned.  This allows mode authors
  609.  #    to keep everything self-contained.
  610.  # -------------------------------------------------------------------------
  611.  ##
  612. proc commentCharacters {purpose} {
  613.     global mode commentCharacters
  614.     global ${mode}::commentCharacters
  615.     # allows a mode to define these things itself.
  616.     if {[info exists ${mode}::commentCharacters(${purpose})]} {
  617.     return [set ${mode}::commentCharacters(${purpose})]
  618.     }    
  619.     if {[info exists commentCharacters(${mode}:${purpose})]} {
  620.     return $commentCharacters(${mode}:${purpose})
  621.     }    
  622.     switch -- $purpose {
  623.     "General" {
  624.         switch -- $mode {
  625.         "TeX" {return "%" }
  626.         "Text" {return "!" }
  627.         "Fort" {return "C" }
  628.         "Scil" {return "//" }
  629.         "Perl" -
  630.         "Tcl" {return "\#" }
  631.         "C" {return "*" }
  632.         "Java" -
  633.         "C++" {return [list "*" "//"] }
  634.         "HTML" {return "<!--"}
  635.         default {
  636.             return
  637.         }
  638.         }
  639.     }        
  640.     "Paragraph" {        
  641.         switch -- $mode {
  642.         "TeX" {return [list "%% " " %%" " % "] }
  643.         "Text" {return [list "!! " " !!" " ! "] }
  644.         "Fort" {return [list "CC " " CC" " C "] }
  645.         "Scil" {return [list "//" "//" "//"] }
  646.         "Perl" -
  647.         "Tcl" {return [list "## " " ##" " # "] }
  648.         "Java" -
  649.         "C" -
  650.         "C++" {return [list "/* " " */" " * "] }
  651.         "HTML" { return [list "<!--" "-->" "|" ] }
  652.         default {
  653.             message "I don't know what comments should look like in this mode.  Sorry."
  654.             error "No comment characters"
  655.         }
  656.         }
  657.     }
  658.     "Box" {
  659.         switch -- $mode {
  660.         "TeX" {return [list "%" 1 "%" 1 "%" 3] }
  661.         "Text" {return [list "!" 1 "!" 1 "!" 3] }
  662.         "Fort" {return [list "C" 1 "C" 1 "C" 3] }
  663.         "Scil" {return [list "//" 2 "//"  2 "//" 3] }
  664.         "Perl" -
  665.         "Tcl" {return [list "#" 1 "#" 1 "#" 3] }
  666.         "Java" -
  667.         "C" -
  668.         "C++" {return [list "/*" 2 "*/" 2 "*" 3] }
  669.         "HTML" { return [list "<!--" 4 "-->" 3 "|" 3] }
  670.         default {
  671.             message "I don't know what comments should look like in this mode.  Sorry."
  672.             error "No comment characters"
  673.         }
  674.         }    
  675.     }
  676.     }    
  677.     
  678. }
  679.  
  680. ## 
  681.  # Default is to look for a    paragraph to comment out.
  682.  # If sent '1',    then we    look for a commented region    to 
  683.  # uncomment.
  684.  ##
  685. proc commentGetRegion { purpose {uncomment 0 } } {
  686.     if {[pos::compare [getPos] != [selEnd]]} {
  687.     watchCursor
  688.     return 0
  689.     }
  690.     
  691.     # there's no selection, so we try and generate one
  692.     
  693.     set pos [getPos]
  694.     if {$uncomment} {
  695.     # uncommenting
  696.     set commentList [commentCharacters $purpose]
  697.     if { [llength $commentList] == 0 } { return 1}
  698.     switch -- $purpose {
  699.         "Box" {
  700.         set begComment [lindex $commentList 0]
  701.         set begComLen [lindex $commentList 1]
  702.         set endComment [lindex $commentList 2]
  703.         set endComLen [lindex $commentList 3]
  704.         set fillChar [lindex $commentList 4]
  705.         set spaceOffset [lindex $commentList 5]
  706.         
  707.         # get length of current line
  708.         set line [getText [lineStart $pos] [nextLineStart $pos] ]
  709.         set c [string trimleft $line]
  710.         set slen [expr {[string length $line] - [string length $c]}]
  711.         set start [string range $line 0 [expr {$slen -1 }] ]
  712.                 
  713.         set pos [getPos]
  714.                 
  715.         if { $start == "" } {
  716.             set p $pos
  717.             while { [string first $fillChar $line] == 0 && \
  718.               [expr {[string last $fillChar $line] + [string length $fillChar]}] \
  719.               >= [string length [string trimright $line]] } {
  720.             set p [nextLineStart $p]
  721.             set line [getText [lineStart $p] [nextLineStart $p]]
  722.             }
  723.             set end [lineStart $p]
  724.             
  725.             set p $pos
  726.             set line "${fillChar}"
  727.             while { [string first $fillChar $line] == 0 && \
  728.               [expr {[string last $fillChar $line] + [string length $fillChar]}] \
  729.               >= [string length [string trimright $line]] } {
  730.             set p [prevLineStart $p]
  731.             set line [getText [prevLineStart $p] [lineStart $p] ]
  732.             }
  733.             set begin [prevLineStart $p]
  734.             
  735.         } else {
  736.             set line "$start"
  737.             set p $pos
  738.             while { [string range $line 0 [expr {$slen -1}] ] == "$start" } {
  739.             set p [nextLineStart $p]
  740.             set line [getText [lineStart $p] [nextLineStart $p]]
  741.             }
  742.             set end [prevLineStart $p]
  743.             
  744.             set p $pos
  745.             set line "$start"
  746.             while { [string range $line 0 [expr {$slen -1}] ] == "$start" } {
  747.             set p [prevLineStart $p]
  748.             set line [getText [prevLineStart $p] [lineStart $p] ]
  749.             }
  750.             set begin [lineStart $p]
  751.         }
  752.         
  753.         set beginline [getText $begin [nextLineStart  $begin]]
  754.         if { [string first "$begComment" "$beginline" ] != $slen } {
  755.             message "First line failed"
  756.             return 1
  757.         }
  758.         
  759.         set endline [getText $end [nextLineStart $end]]
  760.         set epos [string last "$endComment" "$endline"]
  761.         incr epos [string length $endComment]
  762.         set s [string range $endline $epos end ]
  763.         set s [string trimright $s]
  764.         
  765.         if { $s != "" } {
  766.             message "Last line failed"
  767.             return 1
  768.         }
  769.         
  770.         set end [nextLineStart $end]
  771.         select $begin $end
  772.         #alertnote "Sorry auto-box selection not yet implemented"
  773.         }
  774.         "Paragraph" {
  775.         set begComment [lindex $commentList 0]
  776.         set endComment [lindex $commentList 1]
  777.         set fillChar [lindex $commentList 2]
  778.                 
  779.         ## 
  780.          # basic idea is search    back and forwards for lines
  781.          # that    don't begin    the    same way and then see if they
  782.          # match the idea of the beginning and end of a    block
  783.          ##
  784.         
  785.         set line [getText [lineStart $pos] [nextLineStart $pos] ]
  786.         set chk [string range $line 0 [string first $fillChar $line]]
  787.         if { [string trimleft $chk] != "" } {
  788.             message "Not in a comment block"
  789.             return 1
  790.         }
  791.         regsub -all {    } $line " " line
  792.         set p [string first "$fillChar" "$line"]
  793.         set start [string range "$line" 0 [expr {$p + [string length $fillChar] -1}]]
  794.         set ll [commentGetFillLines $start]
  795.         set begin [lindex $ll 0]
  796.         set end [lindex $ll 1]
  797.         
  798.         set beginline [getText $begin [nextLineStart  $begin]]
  799.         if {[string first "$begComment" "$beginline" ] != $p } {
  800.             message "First line failed"
  801.             return 1
  802.         }
  803.                 
  804.         set endline [getText $end [nextLineStart $end]]
  805.         set epos [string last "$endComment" "$endline"]
  806.         incr epos [string length $endComment]
  807.         set s [string range $endline $epos end ]
  808.         set s [string trimright $s]
  809.         
  810.         if { $s != "" } {
  811.             message "Last line failed"
  812.             return 1
  813.         }
  814.         #goto $end
  815.         set end [nextLineStart $end]
  816.         select $begin $end
  817.         }
  818.     }
  819.     } else {
  820.     # commenting out
  821.     set searchString "^\[ \t\]*\$"
  822.     set searchResult1 [search -s -f 0 -r 1 -n $searchString $pos]
  823.     set searchResult2 [search -s -f 1 -r 1 -n $searchString $pos]
  824.     if {[llength $searchResult1]} {
  825.         set posStart [pos::math [lindex $searchResult1 1] + 1]
  826.     } else {
  827.         set posStart [minPos]
  828.     }
  829.     if {[llength $searchResult2]} {
  830.         set posEnd [lindex $searchResult2 0]
  831.     } else {
  832.         set posEnd [pos::math [maxPos] + 1]
  833.         goto [maxPos]
  834.         insertText "\n"
  835.     }
  836.     select $posStart $posEnd
  837.     }
  838.     
  839.     set str "Do you wish to "
  840.     if {$uncomment} { append str "uncomment" } else { append str "comment out" }
  841.     append str " this region?"
  842.     return [expr {![dialog::yesno $str]}]
  843. }
  844.  
  845.  
  846. proc prevLineStart { pos } {
  847.     return [lineStart [pos::math [lineStart $pos] - 1]]
  848. }
  849.  
  850. proc commentSameStart { line start } {
  851.     regsub -all "\t" "$line" " " line
  852.     if { [string first "$start" "$line"] == 0 } {
  853.     return 1
  854.     } else {
  855.     return 0
  856.     }
  857. }
  858.  
  859. proc commentGetFillLines { start } {
  860.     set pos [getPos]
  861.     regsub -all "\t" $start " " start
  862.     set line "$start"
  863.     
  864.     set p $pos
  865.     while { [commentSameStart "$line" "$start"] } {
  866.     set p [nextLineStart $p]
  867.     set line [getText [lineStart $p] [nextLineStart $p]]
  868.     }
  869.     set end [lineStart $p]
  870.     
  871.     set p $pos
  872.     set line "$start"
  873.     while { [commentSameStart "$line" "$start"] } {
  874.     set p [prevLineStart $p]
  875.     set line [getText [prevLineStart $p] [lineStart $p] ]
  876.     }
  877.     set begin [prevLineStart $p]
  878.     return [list $begin $end]
  879. }
  880.  
  881. ## 
  882.  # Author: Vince Darley    <mailto:darley@fas.harvard.edu> 
  883.  ##
  884.  
  885. proc commentParagraph {} {
  886.  
  887. # Preliminaries
  888.     if {[commentGetRegion Paragraph]} { return }
  889.     
  890.     set commentList [commentCharacters Paragraph]
  891.     if { [llength $commentList] == 0 } { return }
  892.  
  893.     set begComment [lindex $commentList 0]
  894.     set endComment [lindex $commentList 1]
  895.     set fillChar [lindex $commentList 2]
  896.     
  897.     
  898.     # First make sure we grab a full block of lines and adjust highlight
  899.     
  900.     set start [getPos]
  901.     set start [lineStart $start]
  902.     set end [selEnd]
  903.     set end [nextLineStart [pos::math $end - 1]]
  904.     select $start $end
  905.     
  906.     # Now get rid of any tabs
  907.     
  908.     if {[pos::compare $end < [maxPos]] } {
  909.         createTMark stopComment [pos::math $end + 1]
  910.         tabsToSpaces
  911.         gotoTMark stopComment
  912.         set end [pos::math [getPos] - 1]
  913.         removeTMark stopComment
  914.     } else {
  915.         tabsToSpaces
  916.         set end [maxPos]
  917.     }
  918.     select $start $end
  919.     set text [getText $start $end]
  920.     
  921. # Next turn it into a list of lines--possibly drop an empty 'last line'
  922.  
  923.     set lineList [split $text "\r\n"]
  924.     set ll [llength $lineList]
  925.     if { [lindex $lineList end] == {} } {
  926.         set lineList [lrange $lineList 0 [expr {$ll -2}] ]
  927.     }
  928.     set numLines [llength $lineList]
  929.     
  930.     # Find left margin for these lines
  931.     set lmargin 100
  932.     for { set i 0 } { $i < $numLines } { incr i } {
  933.         set l [lindex $lineList $i]
  934.         set lm [expr {[string length $l] - [string length [string trimleft $l]]}]
  935.         if { $lm < $lmargin } { set lmargin $lm }
  936.     }
  937.     set ltext ""
  938.     for { set i 0 } { $i < $lmargin } { incr i } {
  939.         append ltext " "
  940.     }
  941.     
  942.     # For each line add stuff on left and concatenate everything into 'text'. 
  943.     
  944.     set text ${ltext}${begComment}\r
  945.     
  946.     for { set i 0 } { $i < $numLines } { incr i } {
  947.         append text ${ltext} ${fillChar} [string range [lindex $lineList $i] $lmargin end] \r
  948.     }
  949.     append text ${ltext} ${endComment} \r
  950.     
  951.     # Now replace the old stuff, turn spaces to tabs, and highlight
  952.     
  953.     replaceText $start $end $text
  954.     set end [pos::math $start + [string length $text]]
  955.     frontSpacesToTabs $start $end
  956. }
  957.  
  958. ## 
  959.  # Author: Vince Darley    <darley@fas.harvard.edu>
  960.  ##
  961.  
  962. proc uncommentParagraph {} {
  963.  
  964.     # Preliminaries
  965.     if {[commentGetRegion Paragraph 1]} { return }
  966.     
  967.     set commentList [commentCharacters Paragraph]
  968.     if { [llength $commentList] == 0 } { return }
  969.     
  970.     set begComment [lindex $commentList 0]
  971.     set endComment [lindex $commentList 1]
  972.     set fillChar [lindex $commentList 2]
  973.     
  974.     set aSpace " "
  975.     set aTab \t
  976.     
  977.     # First make sure we grab a full block of lines and adjust highlight
  978.     
  979.     set start [getPos]
  980.     set start [lineStart $start]
  981.     set end [selEnd]
  982.     set end [nextLineStart [pos::math $end - 1]]
  983.     select $start $end
  984.     set text [getText $start $end]
  985.     
  986.     # Find left margin for these lines
  987.     set l [string range $text 0 [string first "\r" $text] ]
  988.     set lmargin [expr {[string length $l] - [string length [string trimleft $l]]}]
  989.     
  990.     # Make sure we're at the start and end of the paragraph
  991.  
  992.     set startOK [string first $begComment $text]
  993.     set endOK [string last $endComment $text]
  994.     set textLength [string length $text]
  995.     if { $startOK != $lmargin || ($endOK != [expr {$textLength-[string length $endComment]-1}] || $endOK == -1) } {
  996.         alertnote "You must highlight the entire comment paragraph, including the tail ends."
  997.         return
  998.     }
  999.     
  1000.     # Now get rid of any tabs
  1001.     
  1002.     if {[pos::compare $end < [maxPos]]} {
  1003.         createTMark stopComment [pos::math $end + 1]
  1004.         tabsToSpaces
  1005.         gotoTMark stopComment
  1006.         set end [pos::math [getPos] - 1]
  1007.         removeTMark stopComment
  1008.     } else {
  1009.         tabsToSpaces
  1010.         set end [maxPos]
  1011.     }
  1012.     select $start $end
  1013.     set text [getText $start $end]
  1014.     
  1015.     # Next turn it into a list of lines--possibly drop an empty 'last line'
  1016.     
  1017.     set lineList [split $text "\r\n"]
  1018.     set ll [llength $lineList]
  1019.     if { [lindex $lineList end] == {} } {
  1020.         set lineList [lrange $lineList 0 [expr {$ll -2}] ]
  1021.     }
  1022.     set numLines [llength $lineList]
  1023.     
  1024.     # Delete the first and last lines, recompute number of lines
  1025.     
  1026.     set lineList [lreplace $lineList [expr {$numLines-1}] [expr {$numLines-1}] ]
  1027.     set lineList [lreplace $lineList 0 0 ]
  1028.     set numLines [llength $lineList]
  1029.     
  1030.     # get the left margin
  1031.     set lmargin [string first $fillChar [lindex $lineList 0]]
  1032.     set ltext ""
  1033.     for { set i 0 } { $i < $lmargin } { incr i } {
  1034.         append ltext " "
  1035.     }
  1036.     
  1037.     # For each line trim stuff on left and spaces and stuff on right and splice
  1038.     set eliminate $fillChar$aSpace$aTab
  1039.     set dropFromLeft [expr {[string length $fillChar] + $lmargin}]
  1040.     set text ""
  1041.     for { set i 0 } { $i < $numLines } { incr i } {
  1042.         set thisLine [lindex $lineList $i]
  1043.         set thisLine [string trimright $thisLine $eliminate]
  1044.         set thisLine ${ltext}[string range $thisLine $dropFromLeft end]
  1045.         append text $thisLine \r
  1046.     }
  1047.     
  1048.     # Now replace the old stuff, turn spaces to tabs, and highlight
  1049.     
  1050.     
  1051.     replaceText    $start $end    $text
  1052.     set    end [pos::math $start + [string length $text]]
  1053.     frontSpacesToTabs $start $end
  1054. }
  1055.  
  1056.  
  1057. proc frontTabsToSpaces { start end } {
  1058.     select $start $end
  1059.     tabsToSpaces
  1060. }
  1061.  
  1062. proc frontSpacesToTabs { start end } {
  1063.     getWinInfo a
  1064.     set sp [string range "              " 1 $a(tabsize) ]
  1065.     set from [lindex [posToRowCol $start] 0]
  1066.     set to [lindex [posToRowCol $end] 0]
  1067.     while {$from <= $to} {
  1068.     set pos [rowColToPos $from 0]
  1069.     # get the leading whitespace of the current line
  1070.     set res [search -s -n -f 1 -r 1 "^\[ \t\]*" $pos]
  1071.     regsub -all "($sp| +\t)" [eval getText $res] "\t" front
  1072.     eval replaceText $res [list $front]
  1073.     incr from
  1074.     }
  1075. }
  1076.  
  1077. proc forwardDeleteUntil {{c ""}} {
  1078.     if {$c == ""} {
  1079.     message "Forward delete up to next:"
  1080.     set c [getChar]
  1081.     }
  1082.     set p [lindex [search -s -n -f 1 -r 1 [quote::Regfind $c] [getPos]] 0]
  1083.     if {$p != ""} {
  1084.     deleteText [getPos] [pos::math $p + 1]
  1085.     }
  1086. }
  1087.  
  1088. proc forwardDeleteWhitespace {} {
  1089.     set p [lindex [search -s -n -f 1 -r 1 {[^ \t\r\n]} [getPos]] 0]
  1090.     if {$p != ""} {
  1091.     deleteText [getPos] $p
  1092.     }
  1093. }
  1094.  
  1095.